using System;
using Microsoft.Web.Administration;
using Microsoft.Web.Management.Server;
using Microsoft.Web.Management.Iis.Client;

namespace Microsoft.Web.Management.Iis.Server
{
    /// <summary>
    /// Subclass of a ModuleService that provides ModuleServiceMethod's for communicating through many module service methods.
    /// </summary>
    public class LoggingModuleService : ModuleService
    {
        /********************************************************************************************
           STATIC FIELDS
         *******************************************************************************************/
        private const String LoggingConfigurationSection = "system.applicationHost/log";
        private const String HttpLoggingConfigurationSection = "system.webServer/httpLogging";

        /********************************************************************************************
           METHODS 
         *******************************************************************************************/
        
        /// <summary>
        /// Determines if logging is enabled.
        /// </summary>
        /// <param name="section">A LogConfigurationSection object instance.</param>
        /// <param name="httpSection">An HttpLoggingSection object instance.</param>
        /// <param name="siteSection">A SiteLogFile object instance.</param>
        /// <returns></returns>
        private static bool GetLoggingEnabled(
            LogConfigurationSection section,
            HttpLoggingSection httpSection,
            SiteLogFile siteSection)
        {
            // make sure logging is not turned off
            if (!httpSection.DontLog)
            {
                return (((section.LoggingMode == LoggingMode.Binary) &&
                            section.BinaryLogFile.Enabled) ||
                         ((section.LoggingMode == LoggingMode.W3C) &&
                            section.W3CLogFile.Enabled) ||
                         ((section.LoggingMode == LoggingMode.Site) &&
                            siteSection.Enabled));
            }
            return false;
        }

        /// <summary>
        /// Gets a typed configuration section.
        /// </summary>
        /// <param name="sectionName">The name of the configuration section to return.</param>
        /// <param name="sectionType">The strong type of the configuration section to return.</param>
        /// <returns>A ConfigurationSection object instance.</returns>
        private ConfigurationSection GetSection(String sectionName, Type sectionType)
        {
            ManagementConfiguration config = ManagementUnit.Configuration;
            if (config != null)
            {
                ConfigurationSection section = config.GetSection(sectionName, sectionType);
                if (section == null)
                {
                    // TODO: Handle exception case.
                    // BUG: Handle exception case.
                }
                return section;
            }
            RaiseException("LoggingModuleServiceGetSectionFailed");
            return null;
        }


        /// <summary>
        /// Gets the configuration settings from the configuration system.
        /// </summary>
        /// <returns>A property bag that contains the configuration system data.</returns>
        [ModuleServiceMethod(PassThrough = true)]
        public PropertyBag GetConfigurationSettings(  )
        {
            // One property bag per section + aggregated property
            var allProperties = new PropertyBag();
            var httpProperites = new PropertyBag();
            var siteProperties = new PropertyBag();
            var loggingProperties = new PropertyBag();
            var binaryProperties = new PropertyBag();
// ReSharper disable InconsistentNaming
            var w3cProperites = new PropertyBag();
// ReSharper restore InconsistentNaming

            Boolean isServerScope = 
                (base.ManagementUnit.ConfigurationPath.PathType == ConfigurationPathType.Server);

            // Section: system.webServer/httpLogging
            //===================================================================
            var httpSection = GetSection(
                HttpLoggingConfigurationSection,
                typeof(HttpLoggingSection)) as HttpLoggingSection;
            if (httpSection != null)
            {
                httpProperites.Add(LoggingPropertyIndexes.HTTP_DONT_LOG_INDEX, httpSection.DontLog);
                httpProperites.Add(LoggingPropertyIndexes.HTTP_SELECTIVE_LOGGING_INDEX, httpSection.SelectiveLogging);
                httpProperites.Add(LoggingPropertyIndexes.IS_LOCKED_INDEX, httpSection.IsLocked);
            }

            allProperties.Add(LoggingPropertyIndexes.HTTP_ELEMENT_INDEX, httpProperites);

            // Section: system.applicationHost/log/sites/siteDefaults/logFile
            //      OR  system.applicationHost/log/sites/site/logFile
            //===================================================================
            SiteLogFile siteSection;
            if (base.ManagementUnit.ConfigurationPath.PathType == ConfigurationPathType.Server)
            {
                siteSection = base.ManagementUnit.ServerManager.SiteDefaults.LogFile;
            }
            else
            {
                if (base.ManagementUnit.ServerManager.Sites[
                    base.ManagementUnit.ConfigurationPath.SiteName] == null)
                {
                    // TODO: Handle exception case.
                    // BUG: Handle exception case.
                }
                siteSection = base.ManagementUnit.ServerManager.Sites[
                    base.ManagementUnit.ConfigurationPath.SiteName].LogFile;
            }
            siteProperties.Add(LoggingPropertyIndexes.ENABLED_INDEX, siteSection.Enabled);
            siteProperties.Add(LoggingPropertyIndexes.PERIOD_INDEX, (Int32)siteSection.Period);
            siteProperties.Add(LoggingPropertyIndexes.DIRECTORY_INDEX, siteSection.Directory);
            siteProperties.Add(LoggingPropertyIndexes.TRUNCATE_SIZE_INDEX, siteSection.TruncateSize);
            siteProperties.Add(LoggingPropertyIndexes.LOCAL_TIME_ROLLOVER_INDEX, siteSection.LocalTimeRollover);
            siteProperties.Add(LoggingPropertyIndexes.LOG_EXT_FILE_FLAGS_INDEX, (Int32)siteSection.LogExtFileFlags);
            siteProperties.Add(LoggingPropertyIndexes.LOG_FILE_FORMAT_INDEX, (Int32)siteSection.LogFormat);
            allProperties.Add( isServerScope
                    ? LoggingPropertyIndexes.SITE_DEFAULTS_ELEMENT_INDEX
                    : LoggingPropertyIndexes.SITE_ELEMENT_INDEX, siteProperties);

            // Section: system.applicationHost/log
            //===================================================================
            var logSection = GetSection(
                LoggingConfigurationSection,
                typeof(LogConfigurationSection)) as LogConfigurationSection;
            if (logSection != null)
            {
                loggingProperties.Add(LoggingPropertyIndexes.CENTRAL_LOG_FILE_MODE_INDEX, logSection.LoggingMode);
                loggingProperties.Add(LoggingPropertyIndexes.CENTRAL_LOG_IN_UTF8_INDEX, logSection.LogInUtf8);
                loggingProperties.Add(LoggingPropertyIndexes.IS_LOCKED_INDEX, logSection.IsLocked);

                // Section: system.applicationHost/log
                //===================================================================
                if (isServerScope)
                {
                    // Section: system.applicationHost/log/centralBinaryLogging
                    //===================================================================
                    var binaryElement = logSection.BinaryLogFile;
                    binaryProperties.Add(LoggingPropertyIndexes.ENABLED_INDEX, binaryElement.Enabled);
                    binaryProperties.Add(LoggingPropertyIndexes.PERIOD_INDEX, binaryElement.RolloverPeriod);
                    binaryProperties.Add(LoggingPropertyIndexes.DIRECTORY_INDEX, binaryElement.Directory);
                    binaryProperties.Add(LoggingPropertyIndexes.TRUNCATE_SIZE_INDEX, binaryElement.TruncateSize);
                    binaryProperties.Add(LoggingPropertyIndexes.LOCAL_TIME_ROLLOVER_INDEX, binaryElement.LocalTimeRollover);

                    // Section: system.applicationHost/log/centralW3cLogging
                    //===================================================================
// ReSharper disable InconsistentNaming
                    var w3cElement = logSection.W3CLogFile;
// ReSharper restore InconsistentNaming
                    if (w3cElement == null) throw new NullReferenceException();
                    w3cProperites.Add(LoggingPropertyIndexes.ENABLED_INDEX, w3cElement.Enabled);
                    w3cProperites.Add(LoggingPropertyIndexes.PERIOD_INDEX, w3cElement.RolloverPeriod);
                    w3cProperites.Add(LoggingPropertyIndexes.DIRECTORY_INDEX, w3cElement.Directory);
                    w3cProperites.Add(LoggingPropertyIndexes.TRUNCATE_SIZE_INDEX, w3cElement.TruncateSize);
                    w3cProperites.Add(LoggingPropertyIndexes.LOCAL_TIME_ROLLOVER_INDEX, w3cElement.LocalTimeRollover);
                    w3cProperites.Add(LoggingPropertyIndexes.LOG_EXT_FILE_FLAGS_INDEX, w3cElement.LogExtFileFlags);

                    allProperties.Add(LoggingPropertyIndexes.ENABLED_INDEX,
                                      GetLoggingEnabled(logSection, httpSection, siteSection));
                }
            }

            // All Sections
            //===================================================================
            allProperties.Add(LoggingPropertyIndexes.LOG_ELEMENT_INDEX, loggingProperties);
            allProperties.Add(LoggingPropertyIndexes.BINARY_ELEMENT_INDEX, binaryProperties);
            allProperties.Add(LoggingPropertyIndexes.W3C_ELEMENT_INDEX, w3cProperites);

            return allProperties;
        }

        /// <summary>
        /// Updates the HTTP logging settings.
        /// </summary>
        /// <param name="updatedBag">The PropertyBag that contains the values for HTTP logging.</param>
        /// <returns>true if the update operation was successful, otherwise false.</returns>
        [ModuleServiceMethod(PassThrough = true)]
        public Boolean UpdateHttpLogSettings(PropertyBag updatedBag)
        {
            Boolean temp = true;
            try
            {
                var httpSection = GetSection(
                    HttpLoggingConfigurationSection,
                    typeof(HttpLoggingSection)) as HttpLoggingSection;
                if (httpSection != null)
                {
                    httpSection.DontLog = (Boolean)updatedBag[LoggingPropertyIndexes.HTTP_DONT_LOG_INDEX];
                    httpSection.SelectiveLogging = (SelectiveLogging)updatedBag[LoggingPropertyIndexes.HTTP_SELECTIVE_LOGGING_INDEX];
                }
                else
                {
                    temp = false;
                }
            }
            catch
            {
                temp = false;
            }
            base.ManagementUnit.Update();
            return temp;
        }

        /// <summary>
        /// Updates the Site logging settings.
        /// </summary>
        /// <param name="updatedBag">The PropertyBag that contains the values for site logging.</param>
        /// <returns>true if the update operation was successful, otherwise false.</returns>
        [ModuleServiceMethod(PassThrough = true)]
        public Boolean UpdateSiteSettings(PropertyBag updatedBag)
        {
            Boolean temp = true;
            try
            {
                SiteLogFile siteSection;
                if (base.ManagementUnit.ConfigurationPath.PathType == ConfigurationPathType.Server)
                {
                    siteSection = base.ManagementUnit.ServerManager.SiteDefaults.LogFile;
                }
                else
                {
                    if (base.ManagementUnit.ServerManager.Sites[
                        base.ManagementUnit.ConfigurationPath.SiteName] == null)
                    {
                        temp = false;
                        // TODO: Handle exception case.
                        // BUG: Handle exception case.
                    }
                    siteSection = base.ManagementUnit.ServerManager.Sites[
                        base.ManagementUnit.ConfigurationPath.SiteName].LogFile;
                }
                siteSection.Enabled = (Boolean)updatedBag[LoggingPropertyIndexes.ENABLED_INDEX];
                siteSection.Period = (LoggingRolloverPeriod)updatedBag[LoggingPropertyIndexes.PERIOD_INDEX];
                siteSection.Directory = (String)updatedBag[LoggingPropertyIndexes.DIRECTORY_INDEX];
                siteSection.TruncateSize = (Int64)updatedBag[LoggingPropertyIndexes.TRUNCATE_SIZE_INDEX];
                siteSection.LocalTimeRollover = (Boolean)updatedBag[LoggingPropertyIndexes.LOCAL_TIME_ROLLOVER_INDEX];
                siteSection.LogExtFileFlags = (LogExtFileFlags)updatedBag[LoggingPropertyIndexes.LOG_EXT_FILE_FLAGS_INDEX];
                siteSection.LogFormat = (LogFormat)updatedBag[LoggingPropertyIndexes.LOG_FILE_FORMAT_INDEX];
            }
            catch
            {
                temp = false;
            }
            base.ManagementUnit.Update();
            return temp;
        }

        /// <summary>
        /// Updates the application host logging settings.
        /// </summary>
        /// <param name="updatedBag">The PropertyBag that contains the values for site logging.</param>
        /// <returns>true if the update operation was successful, otherwise false.</returns>
        [ModuleServiceMethod(PassThrough = true)]
        public Boolean UpdateApplicationHostLogSettings(PropertyBag updatedBag)
        {
            Boolean temp = true;
            try
            {
                var logSection = GetSection(
                    LoggingConfigurationSection,
                    typeof(LogConfigurationSection)) as LogConfigurationSection;
                if (logSection != null)
                {
                    logSection.LoggingMode = (LoggingMode)updatedBag[LoggingPropertyIndexes.CENTRAL_LOG_FILE_MODE_INDEX];
                    logSection.LogInUtf8 = (Boolean)updatedBag[LoggingPropertyIndexes.CENTRAL_LOG_IN_UTF8_INDEX];
                }
                else
                {
                    temp = false;
                }
            }
            catch
            {
                temp = false;
            }
            base.ManagementUnit.Update();
            return temp;
        }

        /// <summary>
        /// Updates the central binary logging settings.
        /// </summary>
        /// <param name="updatedBag">The PropertyBag that contains the values for central binary logging.</param>
        /// <returns>true if the update operation was successful, otherwise false.</returns>
        [ModuleServiceMethod(PassThrough = true)]
        public Boolean UpdateCentralBinaryLogSettings(PropertyBag updatedBag)
        {
            Boolean temp = true;
            try
            {
                var logSection = GetSection(
                    LoggingConfigurationSection,
                    typeof(LogConfigurationSection)) as LogConfigurationSection;
                if (logSection != null)
                {
                    logSection.BinaryLogFile.Enabled = (Boolean)updatedBag[LoggingPropertyIndexes.ENABLED_INDEX];
                    logSection.BinaryLogFile.RolloverPeriod = (LoggingRolloverPeriod)updatedBag[LoggingPropertyIndexes.PERIOD_INDEX];
                    logSection.BinaryLogFile.Directory = (String)updatedBag[LoggingPropertyIndexes.DIRECTORY_INDEX];
                    logSection.BinaryLogFile.TruncateSize = (Int64)updatedBag[LoggingPropertyIndexes.TRUNCATE_SIZE_INDEX];
                    logSection.BinaryLogFile.LocalTimeRollover = (Boolean)updatedBag[LoggingPropertyIndexes.LOCAL_TIME_ROLLOVER_INDEX];
                }
                else
                {
                    temp = false;
                }
            }
            catch
            {
                temp = false;
            }
            base.ManagementUnit.Update();
            return temp;
        }

        /// <summary>
        /// Updates the central W3C logging settings.
        /// </summary>
        /// <param name="updatedBag">The PropertyBag that contains the values for central W3C logging.</param>
        /// <returns>true if the update operation was successful, otherwise false.</returns>
        [ModuleServiceMethod(PassThrough = true)]
        public Boolean UpdateCentralW3CLogSettings(PropertyBag updatedBag)
        {
            Boolean temp = true;
            try
            {
                var logSection = GetSection(
                    LoggingConfigurationSection,
                    typeof(LogConfigurationSection)) as LogConfigurationSection;
                if (logSection != null)
                {
                    logSection.W3CLogFile.Enabled = (Boolean) updatedBag[LoggingPropertyIndexes.ENABLED_INDEX];
                    logSection.W3CLogFile.RolloverPeriod =
                        (LoggingRolloverPeriod) updatedBag[LoggingPropertyIndexes.PERIOD_INDEX];
                    logSection.W3CLogFile.Directory = (String) updatedBag[LoggingPropertyIndexes.DIRECTORY_INDEX];
                    logSection.W3CLogFile.TruncateSize = (Int64) updatedBag[LoggingPropertyIndexes.TRUNCATE_SIZE_INDEX];
                    logSection.W3CLogFile.LocalTimeRollover =
                        (Boolean) updatedBag[LoggingPropertyIndexes.LOCAL_TIME_ROLLOVER_INDEX];
                }
                else
                {
                    temp = false;
                }
            }
            catch
            {
                temp = false;
            }
            base.ManagementUnit.Update();
            return temp;
        }
    }
}
